package com.ac.scum.log.web.model.server.service;

import com.ac.common.core.exception.BaseException;
import com.ac.common.core.exception.config.ExceptionType;
import com.ac.scum.log.bean.Package;
import com.ac.scum.log.bean.*;
import com.ac.scum.log.web.dto.login.LoginDTO;
import com.ac.scum.log.web.model.card.mapper.CardMapper;
import com.ac.scum.log.web.model.card.mapper.CardUsedMapper;
import com.ac.scum.log.web.model.menu.service.MenuService;
import com.ac.scum.log.web.model.pack.mapper.PackageMapper;
import com.ac.scum.log.web.model.server.mapper.ServerAlarmConfigMapper;
import com.ac.scum.log.web.model.server.mapper.ServerMapper;
import com.ac.scum.log.web.model.server.mapper.ServerSysConfigMapper;
import com.ac.scum.log.web.model.user.mapper.UserPreferenceMapper;
import com.ac.scum.log.web.utils.user.CurrentUserUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import io.micrometer.common.util.StringUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@AllArgsConstructor
public class ServerService extends ServiceImpl<ServerMapper, Server> {
    private final ServerMapper serverMapper;
    private final CardMapper cardMapper;
    private final CardUsedMapper cardUsedMapper;
    private final PackageMapper packageMapper;
    private final MenuService menuService;
    private final UserPreferenceMapper userPreferenceMapper;
    private final RedisTemplate<String, Object> redisTemplate;
    private final ServerSysConfigMapper serverSysConfigMapper;
    private final ServerAlarmConfigMapper serverAlarmConfigMapper;


    public List<Server> getServersByUserId(Long userId) {
        return serverMapper.selectList(Wrappers.lambdaQuery(Server.class).inSql(Server::getId, "select server_id from server_admin_user where user_id = " + userId));
    }

    public List<Long> getUserIdsByServerId(Long serverId) {
        return serverMapper.getUserIdsByServerId(serverId);
    }

    public List<Long> getServerIdsByUserId(Long userId) {
        return serverMapper.getServerIdsByUserId(userId);
    }

    @Transactional
    public void save(Server server, Long userId) throws BaseException {
        if (serverMapper.selectCount(Wrappers.lambdaQuery(Server.class).eq(Server::getApiId, server.getApiId())) > 0) {
            throw new BaseException(ExceptionType.Has_Other_Data);
        }
        ServerSysConfig serverSysConfig = new ServerSysConfig();
        ServerAlarmConfig serverAlarmConfig = new ServerAlarmConfig();
        server.setId(null);
        serverMapper.save(server, userId);
        serverSysConfig.setServerId(server.getId());
        serverAlarmConfig.setServerId(server.getId());
        //生成服务器配置
        serverSysConfigMapper.insert(serverSysConfig);
        //生成服务器预警配置
        serverAlarmConfigMapper.insert(serverAlarmConfig);
    }

    @Transactional
    public boolean active(Long serverId, String keyCard) throws BaseException {
        Server server = getById(serverId);
        if (server == null) {
            throw new BaseException(ExceptionType.Parameter_Error);
        }
        Card card = cardMapper.selectOne(Wrappers.lambdaQuery(Card.class).eq(Card::getKeyCard, keyCard));
        if (card == null || card.getUsed()) {
            throw new BaseException(ExceptionType.Card_Not_Found);
        }

        if (card.getType().equals(2) && cardUsedMapper.selectCount(Wrappers.lambdaQuery(CardUsed.class).eq(CardUsed::getCardId, card.getId()).eq(CardUsed::getServerId, serverId)) > 0) {
            throw new BaseException(ExceptionType.System_Error.of("服务器已使用过该卡密"));
        }

        Package pack = packageMapper.selectByServerIdAndPackageId(serverId, card.getPackageId());
        if (pack != null && pack.getId().equals(card.getPackageId())) {
            pack.setExpirationTime((pack.getExpirationTime().isBefore(LocalDateTime.now()) ? LocalDateTime.now() : pack.getExpirationTime()).plusHours(card.getHours()));
            packageMapper.updatePackageAsServer(pack);
        } else {
            pack = new Package();
            pack.setServerId(serverId);
            pack.setExpirationTime(LocalDateTime.now().plusHours(card.getHours()));
            pack.setId(card.getPackageId());
            packageMapper.insertPackageAsServer(pack);
        }
        if (card.getType() == 1) {
            card.setUsed(true);
            card.setUseTime(LocalDateTime.now());
            card.setUseId(serverId);
            cardMapper.updateById(card);
        }
        if (card.getType() == 2) {
            cardUsedMapper.insert(new CardUsed() {{
                setCardId(card.getId());
                setServerId(serverId);
            }});
        }
        return Boolean.TRUE;
    }

    public Boolean loginUserUseServer(Long serverId) throws BaseException {


        if (!CurrentUserUtil.getCurrentLoginDTO().getAdminUser().getId().equals(1765425375730122753L) && getServersByUserId(CurrentUserUtil.getCurrentLogUser().getId()).stream().noneMatch(server -> server.getId().equals(serverId))) {
            throw new BaseException(ExceptionType.Parameter_Error.of("未知服务器ID"));
        }
        String token = CurrentUserUtil.getCurrentToken();
        LoginDTO loginDto = (LoginDTO) redisTemplate.opsForValue().get(token);
        if (loginDto == null) {
            throw new BaseException(ExceptionType.System_Error);
        }
        loginDto.setMenus(menuService.findMenuByServerId(serverId));
        UserPreference userPreference = loginDto.getUserPreference();
        userPreference.setLastCheckServerId(serverId);
        loginDto.setUserPreference(userPreference);
        redisTemplate.opsForValue().set(token, loginDto, 1, TimeUnit.HOURS);
        userPreferenceMapper.update(Wrappers.lambdaUpdate(UserPreference.class).set(UserPreference::getLastCheckServerId, serverId).eq(UserPreference::getUserId, loginDto.getAdminUser().getId()));
        return Boolean.TRUE;
    }

    public List<Package> getServerPackages(Long serverId) {
        return packageMapper.selectByServerId(serverId);
    }

    public List<Server> getHasAlarmServersByName(String name) {
        String where = " 1=1 ";
        if (StringUtils.isNotBlank(name)) {
            List<Server> servers = this.getBaseMapper().selectList(Wrappers.<Server>lambdaQuery().like(Server::getFullName, name));
            String ids = servers.stream().map(Server::getId).map(String::valueOf).collect(Collectors.joining(","));
            where += " and s.id in (" + ids + ")";
            if (servers.isEmpty()) {
                return Lists.newArrayList();
            }
        }
        return serverMapper.getHasAlarmServersByWhere(where);
    }
}
